package com.ruoyi.business.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.bizsys.domain.*;
import com.ruoyi.bizsys.enums.AwardEventEnum;
import com.ruoyi.bizsys.service.*;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.bizsys.vo.IncreasedLimitLogVo;
import com.ruoyi.business.Vo.RulePremiseVo;
import com.ruoyi.business.common.AsyncService;
import com.ruoyi.business.common.constant.KycApiEnum;
import com.ruoyi.business.common.riskRule.RulePremiseService;
import com.ruoyi.business.common.riskRule.RuleStrategy;
import com.ruoyi.business.domain.*;
import com.ruoyi.business.mapper.BCreditAccountMapper;
import com.ruoyi.business.mapper.BusinessMapper;
import com.ruoyi.business.mapper.ExtIdentityMapper;
import com.ruoyi.business.service.*;
import com.ruoyi.common.VersionUtils;
import com.ruoyi.common.constant.RiskConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.enums.UserTagEnum;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.Func;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.business.CheckAccountNameUtil;
import com.ruoyi.extdata.common.config.OkraConfig;
import com.ruoyi.extdata.common.config.PayStackConfig;
import com.ruoyi.extdata.service.ICrsService;
import com.ruoyi.extdata.service.IExternalApiService;
import com.ruoyi.extdata.service.IOnePipeService;
import com.ruoyi.sms.domain.SmsSendData;
import com.ruoyi.system.service.ISysDictDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: Jamnsin
 * @Date: 2021/3/1 10:39 上午
 */
@Slf4j
@Service
public class CreditServiceImpl extends BaseService implements ICreditService {

    @Autowired
    IExternalApiService externalApiService;

    @Autowired
    ICreditApplyService creditApplyService;

    @Autowired
    IUserActivityGroupService userActivityGroupService;

    @Autowired
    ICreditAccountService creditAccountService;

    @Autowired
    BusinessMapper businessMapper;

    @Autowired
    PayStackConfig payStackConfig;

    @Autowired
    OkraConfig okraConfig;

    @Autowired
    IAppService appService;

    @Autowired
    RedisCache redisCache;

    @Autowired
    IExtIdentityResultService extIdentityResultService;

    @Autowired
    RulePremiseService premiseService;

    @Autowired
    IPayTransService payTransService;

    @Autowired
    IRealNameAuthLogService realNameAuthLogService;

    @Autowired
    IAppDeviceInfoService appDeviceInfoService;

    @Autowired
    IIncreasedLimitLogService increasedLimitLogService;

    @Autowired
    BCreditAccountMapper bCreditAccountMapper;

    @Autowired
    ICrsService crsService;

    @Autowired
    ExtIdentityMapper extIdentityMapper;

    @Autowired
    ISysDictDataService sysDictDataService;

    @Autowired
    IOnePipeService onePipeService;

    @Autowired
    AsyncService asyncService;

    @Autowired
    IBankListService bankListService;

    @Autowired
    ICreditAccountInfoService creditAccountInfoService;

    @Autowired
    ICreditFlagService creditFlagService;

    @Autowired
    private IBankAccountService bankAccountService;

    @Autowired
    IAwardDistributionService awardDistributionService;

    private static List<String> specialOccupationsList = Arrays.asList("1", "2", "6");

    private ThreadLocal<String> kycExqApiThreadLocal = new ThreadLocal<>();

    @Autowired
    private IRealNameService realNameService;

    @Autowired
    private RestTemplateService restTemplateService;
    @Autowired
    IRealNameAuthService iRealNameAuthService;

    @Autowired
    ICustomerAndonService customerAndonService;

    @Autowired
    IAppUserService iAppUserService;

    @Autowired
    RulePremiseService rulePremiseService;

    @Autowired
    IAppDeviceContactapplistRelativeService iAppDeviceContactapplistRelativeService;

    @Override
    public Object preAuth(BVNInfo bvnInfo, JSONObject deviceInfo) {
        log.info("preAuth,入参 BVNInfo：{},JSONObject:{}", bvnInfo, deviceInfo);
        String userId = getUserId();

        //用户失败次数预校验
        int failCount = realNameService.realNameFailPreCheck(userId);
        realNameAuthIsEmpty(bvnInfo.getAccountNumber(), bvnInfo.getBvnAccount(), userId);
        //今日次数预校验
        long applicationCount = realNamePreRiskCheck(userId);

        RealNameAuthLog realNameAuthLog = new RealNameAuthLog();
        realNameAuthLog.setState("1");
        try {
            //bvn格式校验
            RealNameServiceImpl.bvnFormatCheck(bvnInfo.getBvnAccount());
            //保存AppDeviceInfo
            if (deviceInfo != null && StringUtils.isNotBlank(deviceInfo.toString())) {
                appService.saveAppDeviceInfoBoard(userId, getCustNo(userId), BizConstants.ACTION_TYPE_REAL_NAME, "", deviceInfo);
            }

            //校验accountNUmber
            String accountName = payTransService.checkAccountNumber(bvnInfo.getAccountNumber(), bvnInfo.getBankCode());
            String basisName = bvnInfo.getFirstName().trim() + " " + bvnInfo.getMiddleName().trim() + " " + bvnInfo.getLastName().trim();
            //校验姓名
            RealNameServiceImpl.verifyName(basisName, accountName);

            //三方接口校验
            ExtIdentityResult extIdentityResult = extApiVerification(bvnInfo);

            //校验属性
            verifyField(bvnInfo, extIdentityResult);
            //生日校验
            realNameService.dobVerify(bvnInfo.getBirthDate(), extIdentityResult.getDob(), realNameAuthLog);

            //注册手机号与BVN手机号不一致
            JSONObject questionJson = verifyIdentity(bvnInfo, extIdentityResult);
            if (Func.isNotEmpty(questionJson.get("unmatch"))) {
                asyncService.apdUserTag(AppUserMark.builder().userId(userId).build(), UserTagEnum.IDENTITY_VERIFIED_FAILED_BVN);
                return questionJson;
            }
            asyncService.apdUserTag(AppUserMark.builder().userId(userId).build(), UserTagEnum.IDENTITY_VERIFIED_FAILED_NOT_ERR);
            //若不满足风控需求 仍然发短信
            if (!realNameRiskCheck(bvnInfo.getFingerprint())) {
                return realNameService.sendSms(null);
            }
        } catch (Exception e) {
            log.error("实名认证异常：", e);
            realNameAuthLog.setErrorMsg(e.getMessage());

            String info1 = "Information does not match, please check your information, ";
            String info2 = "Information does not match, please check your information, ";
            if (e instanceof BusinessException) {
                Integer code = ((BusinessException) e).getCode();
                //接口异常 不计入实名次数
                if (RspEnmu.EXT_API_EXCEPTION.getCode().equals(code)) {
                    realNameAuthLog.setState(BizConstants.REAL_NAME_LOG_SKIP);
                    //网络异常标签
                    UserTagEnum userTagEnum = UserTagEnum.exceptionMapper(e.getMessage());
                    userTagEnum = Optional.ofNullable(userTagEnum).orElse(UserTagEnum.IDENTITY_VERIFIED_FAILED_NET_BUSY);
                    asyncService.apdUserTag(AppUserMark.builder().userId(userId).build(), userTagEnum);
                    throw new BusinessException(RspEnmu.EXT_API_EXCEPTION.getMessage());
                }

                //实名失败标签
                UserTagEnum userTagEnum = UserTagEnum.exceptionMapper(e.getMessage());
                userTagEnum = Optional.ofNullable(userTagEnum).orElse(UserTagEnum.getDiy("Identity verification failed", e.getMessage()));
                asyncService.apdUserTag(AppUserMark.builder().userId(userId).build(), userTagEnum);

                //实名失败
                if (RspEnmu.REAL_NAME_CHECK_FAILED.getCode().equals(code)) {
                    realNameAuthLog.setState(BizConstants.REAL_NAME_LOG_FAIL);
                    int n = 7;
                    if (failCount > 0) {
                        n = 30;
                    }
                    //throw new BusinessException("You can go to the bank to update your information and try again in " + n + " days", RspEnmu.REAL_NAME_CHECK_FAILED_JUMP_PAGE.getRetCode());
                    throw new BusinessException("Please contact the bank to update your information and try again in " + n + " days.", RspEnmu.REAL_NAME_CHECK_FAILED_JUMP_PAGE.getCode());
                }
                //BVN_Account不匹配
                if (RspEnmu.BVN_NOT_EQUALS_ACCOUNT_NUMBER.getCode().equals(code)) {
                    info1 = "BVN does not match, please check your information, ";
                    info2 = "BVN does not match, please check your information, ";
                }
                //验证码当天超次数
                if (RspEnmu.SMS_CODE_COUNT_LIMIT.getCode().equals(code)) {
                    throw new BusinessException(RspEnmu.SMS_CODE_COUNT_LIMIT.getMessage());
                }
            }
            //当前第几次
            applicationCount = applicationCount + 1;
            if (applicationCount == 4) {
                throw new BusinessException(info1 + "please try again 60 minutes later. ");
            }
            if (applicationCount == 5) {
                throw new BusinessException(info1 + "please try again 6 hours later. ");
            }
            if (applicationCount == 6) {
                throw new BusinessException(info1 + "please try again tomorrow. ");
            }
            long maxCount = 4;
            throw new BusinessException(info2 + "you still have " + (maxCount - applicationCount) + " chance. ");
        } finally {
            //保存提交信息至实名日志
            realNameService.saveRealNameAuthLog(bvnInfo, realNameAuthLog, kycExqApiThreadLocal);
        }
        realNameService.processRealNameAuth(true);
        return 200;
    }


    private ExtIdentityResult extApiVerification(BVNInfo bvnInfo) {

        ExtIdentityResult localExtIdentity = getLocalExtIdentity(bvnInfo.getAccountNumber(), bvnInfo.getBvnAccount());
        if (localExtIdentity != null) {
            kycExqApiThreadLocal.set(localExtIdentity.getApiName());
            return localExtIdentity;
        }

        //前端PayStackCode获取所有银行code
        BankCode bankCode = bankListService.getBankCodeByPayStackCode(bvnInfo.getBankCode());
        if (Func.isEmpty(bankCode)) {
            throw new BusinessException(RspEnmu.NOT_SUPPORT_BANK);
        }

        //先查onepipe
        ExtIdentityResult extIdentityResult = onePipeIdentity(bvnInfo, bankCode, getUserId());
        //查到信息 BVN查得
        if (Func.isNotEmpty(extIdentityResult)) {
            try {
                log.info("onepipe_Check");
                verifyField(bvnInfo, extIdentityResult);
                return extIdentityResult;
            } catch (Exception e) {
                //onepipe返回失败失败，说明dob 或 手机号码为空，请求crs & Okra
                if (e instanceof BusinessException) {
                    Integer code = ((BusinessException) e).getCode();
                    //实名失败
                    if (RspEnmu.REAL_NAME_CHECK_FAILED.getCode().equals(code)) {
                        log.info("onepipe 返回失败,{},||{}", RspEnmu.REAL_NAME_CHECK_FAILED.getCode(), code);
                        return identityVerification(bvnInfo, bankCode, "N/A".equals(extIdentityResult.getBvn()));
                    } else {
                        throw e;
                    }
                }
            }
        }
        //查不到信息 查 crs & okraBvn & okra
        return identityVerification(bvnInfo, bankCode, true);
    }

    private ExtIdentityResult onePipeIdentity(BVNInfo bvnInfo, BankCode bankCode, String userId) {

        //mock开关
        if (!kycApiSwitch(KycApiEnum.ONE_PIPE_LOOKUP_ACCOUNT)) {
            return null;
        }

        if (Func.isEmpty(bankCode.getOnepipeBankCode())) {
            log.info("onePipeBankCode为空！");
            return null;
        }
        ExtIdentityResult extIdentityResult = getLocalExtIdentity(bvnInfo.getAccountNumber(), null);
        if (extIdentityResult != null) {
            kycExqApiThreadLocal.set(extIdentityResult.getApiName());
            return extIdentityResult;
        }
        try {
            this.appendReqExtApi("Lookup-Account-Mid");
            JSONObject data = onePipeService.lookupAccount(bvnInfo.getAccountNumber(), bankCode.getOnepipeBankCode(), userId);
            if (data == null) {
                return null;
            }
            extIdentityResult = new ExtIdentityResult();
            extIdentityResult.setApiName("Lookup-Account-Mid");
            data = JSONObject.parseObject(data.get("data").toString());
            JSONObject response = JSONObject.parseObject(data.get("provider_response").toString());
            extIdentityResult.setAccountNumber(bvnInfo.getAccountNumber());
            extIdentityResult.setBankCode(bvnInfo.getBankCode());
            extIdentityResult.setUserId(userId);
            extIdentityResult.setCustNo(getCustNo());
            extIdentityResult.setBvn(Func.jsonToStr(response.get("bvn")));
            extIdentityResult.setDob(Func.jsonToStr(response.get("dob")));
            extIdentityResult.setGender(Func.jsonToStr(response.get("gender")));
            extIdentityResult.setPhones(Func.jsonToStr(response.get("mobile_number")));
            extIdentityResult.setPhone1(Func.jsonToStr(response.get("mobile_number")));
            //逗号转空格
            extIdentityResult.setFullname(CheckAccountNameUtil.commaConvertSpace(Func.jsonToStr(response.get("account_name"))));
            extIdentityResult.setFirstname(CheckAccountNameUtil.commaConvertSpace(Func.jsonToStr(response.get("first_name"))));
            extIdentityResult.setMiddlename(CheckAccountNameUtil.commaConvertSpace(Func.jsonToStr(response.get("middle_name"))));
            extIdentityResult.setLastname(CheckAccountNameUtil.commaConvertSpace(Func.jsonToStr(response.get("last_name"))));
            extIdentityResult.setCurrency(Func.jsonToStr(response.get("account_currency")));
            extIdentityResult.setCustomer(Func.jsonToStr(response.get("customer_id")));

            //Func.hasEmpty(extIdentityResult.getDob(), extIdentityResult.getPhones())
            extIdentityResult.setState(BizConstants.REAL_NAME_LOG_NORMAL);
            if ("N/A".equals(extIdentityResult.getBvn()) || Func.isEmpty(extIdentityResult.getBvn())
                    || Func.isEmpty(extIdentityResult.getPhones())
                    || extIdentityResult.getPhones().length() < 10) {
                extIdentityResult.setState(BizConstants.REAL_NAME_LOG_FAIL);
            }
            extIdentityResultService.insertExtIdentityResult(extIdentityResult);
        } catch (Exception e) {
            log.error("onePipeIdentity error", e);
            this.apiExceptionHandle(KycApiEnum.ONE_PIPE_LOOKUP_ACCOUNT, RspEnmu.EXT_API_EXCEPTION_OKRA_ONE_PIPE, e);
        }
        if (BizConstants.REAL_NAME_LOG_FAIL.equals(extIdentityResult.getState())) {
            return null;
        }
        return extIdentityResult;

    }

    private ExtIdentityResult getLocalExtIdentity(String account, String bvn) {
        ExtIdentityResult extIdentifyResultQ = new ExtIdentityResult();
        if (Func.isNotEmpty(account)) {
            extIdentifyResultQ.setAccountNumber(account);
        }
        if (Func.isNotEmpty(bvn)) {
            extIdentifyResultQ.setBvn(bvn);
        }
        extIdentifyResultQ.setState(BizConstants.BIZ_STATUS_1);
        //不管拿到的是crs还是okra的实名信息，有则用
        List<ExtIdentityResult> extIdentifyResults = extIdentityResultService.selectExtIdentityResultList(extIdentifyResultQ);
        if (extIdentifyResults.size() > 0) {
            return extIdentifyResults.get(0);
        }
        return null;
    }

    @Override
    public ExtIdentityResult dealIdentityVerifyResult(String userId, JSONObject apiRes, BVNInfo bvnInfo, String apiName) {
        ExtIdentityResult extIdentifyResult = getExtIdentityResult(userId, apiRes, bvnInfo.getAccountNumber(), bvnInfo.getBankCode());
        extIdentifyResult.setApiName(apiName);
        //生日或手机号码为空
        if (Func.isEmpty(extIdentifyResult.getPhones())) {
            extIdentifyResult.setState(BizConstants.REAL_NAME_LOG_FAIL);
        }
        extIdentityResultService.insertExtIdentityResult(extIdentifyResult);
        return extIdentifyResult;
    }

    @Override
    public boolean realNameRiskCheck(String fingerprint) {
        RealNameAuthLog realNameAuthLogQ = new RealNameAuthLog();
        realNameAuthLogQ.setUserId(getUserId());
        List<RealNameAuthLog> realNameAuthLogs = realNameAuthLogService.selectRealNameAuthLogList(realNameAuthLogQ);
        if (realNameAuthLogs.size() == 0) {
            return true;
        }
        RealNameAuthLog realNameAuthLog = realNameAuthLogs.get(realNameAuthLogs.size() - 1);
        long time = System.currentTimeMillis() - realNameAuthLog.getCreateTime().getTime();
        long day = time / (24 * 60 * 60 * 1000);
        //当前设备信息与首次实名时的设备信息是否一致 & 提交实名信息的时间是否大于首次提交实名信息的24小时
        return fingerprint.equals(realNameAuthLog.getFingerprint()) && day < 1;
    }

    private JSONObject getBvnInfo(String bvnAccount) {
        return externalApiService.invokePayStackApi("Resolve-BVN-Premium", payStackConfig.getResolveBVNPremium().replace("BVN", bvnAccount), "GET", null, null);
    }


    @Override
    public List<CreditApply> getTempCredotApplyList(String userId) {
        return businessMapper.getTempCredotApplyList(userId);
    }

    @Override
    public List<LoanOrder> getTempLoanOrderList(String custNo) {
        return businessMapper.getTempLoanOrderList(custNo);
    }

    @Override
    public List<RaiseLimitTaskVo> getCreditWithoutLoanList() {
        return businessMapper.getCreditWithoutLoanList();
    }

    @Override
    public String againTrigger(CreditApply creditApply) {
        CreditFlag creditFlagQuery = new CreditFlag();
        creditFlagQuery.setSerialNo(creditApply.getApplySerialNo());
        creditFlagQuery.setType(BizConstants.RULE_TYPE_APPLY);
        List<CreditFlag> creditFlags = creditFlagService.selectCreditFlagList(creditFlagQuery);

        String phone = "";
        String lastName = "";
        String fullName = "";

        QueryWrapper<RealNameAuth> realNameAuthQueryWrapper = new QueryWrapper<>();
        realNameAuthQueryWrapper.eq("cust_no", creditApply.getCustNo()).select("first_name", "middle_name", "last_name", "user_id");
        RealNameAuth realNameAuth = iRealNameAuthService.getOne(realNameAuthQueryWrapper);

        if (realNameAuth != null) {
            fullName = realNameAuth.getFullName();
            lastName = realNameAuth.getLastName();

            //查询用户手机号
            QueryWrapper<AppUser> appUserQueryWrapper = new QueryWrapper<>();
            appUserQueryWrapper.eq("user_uuid", realNameAuth.getUserId()).select("phone");
            AppUser appUser = iAppUserService.getOne(appUserQueryWrapper);
            phone = appUser.getPhone();
        }

        if (creditFlags.size() == 0) {
            return "false";
        }

        if (Func.hasEmpty(creditFlags.get(0).getOrderFlag(), creditFlags.get(0).getDeviceFlag(), creditFlags.get(0).getAppsFlag(), creditFlags.get(0).getContactsFlag())) {

            CreditFlag creditFlag = creditFlags.get(0);

            if (Func.hasEmpty(creditFlag.getAppsFlag(), creditFlag.getContactsFlag())) {
                RulePremiseVo rulePremiseVo = new RulePremiseVo().builder().custNo(creditApply.getCustNo()).serialNo(creditApply.getApplySerialNo()).type("apply").build();
                creditFlag = rulePremiseService.flagProcess(rulePremiseVo, creditFlag);
                String deviceIdByUserId = appDeviceInfoService.getDeviceIdByUserId(realNameAuth.getUserId(), creditFlag.getCreateTime());
                creditFlag = rulePremiseService.flagProcessV228(deviceIdByUserId, realNameAuth.getUserId(), creditFlag);
                if (!Func.hasEmpty(creditFlag.getAppsFlag(), creditFlag.getContactsFlag())) {
                    creditFlagService.updateCreditFlag(creditFlag);
                }
            }

            if (Func.hasEmpty(creditFlag.getOrderFlag(), creditFlag.getDeviceFlag(), creditFlag.getAppsFlag(), creditFlag.getContactsFlag())) {
                CreditApply creditApplyUpddate = new CreditApply();
                creditApplyUpddate.setId(creditApply.getId());
                creditApplyUpddate.setApplyStatus(BizConstants.APPLY_STATUS_10_CANCEL);
                creditApplyUpddate.setUpdateTime(new Date());
                creditApplyService.updateCreditApply(creditApplyUpddate);

                //发送邮件
                customerAndonService.sendAndonEamil(creditFlag, phone, fullName);

                //触发短信
                JSONObject smsParam = new JSONObject();
                smsParam.put("lastName", lastName);

                SmsSendData smsData = SmsSendData.builder().noticeType(BizConstants.SMS_NOTICE_TYPE_NOTICE).smsType(BizConstants.ANDON_CREDIT).phoneNumber(phone).countryCode("+234").smsParam(smsParam).build();
                asyncService.sendSms(smsData);

                return "false";
            }
        }
        String error = "";
        try {
            this.rulePass(creditApply.getApplySerialNo());
            QueryWrapper<CreditApply> creditApplyWrapper = new QueryWrapper<>();
            creditApplyWrapper.eq("apply_serial_no", creditApply.getApplySerialNo());
            creditApply = creditApplyService.getOne(creditApplyWrapper);

        } catch (Exception e) {
            log.error("授信卡单处理异常-流水号:{}", creditApply.getApplySerialNo(), e);
            error = e.getMessage();
        } finally {
            if (creditApply.getApplyStatus() == BizConstants.APPLY_STATUS_9_APPROVALING) {
                JSONObject andonContent = customerAndonService.getAndonContent(creditFlags.get(0), phone, fullName);
                //触发飞书提醒
                customerAndonService.sendAndonFeishu(creditFlags.get(0).getType(), andonContent);
                //触发邮件
                customerAndonService.sendAndonEamil(creditFlags.get(0).getType(), andonContent);
                return error;
            } else {
                return "true";
            }
        }
    }

    /**
     * @param userId
     * @return 当日已申请次数
     */
    private long realNamePreRiskCheck(String userId) {

        //实名提交4次之后等待60分钟后才能再次提交，60分钟后再提交一次后等6小时后才能提交，6小时后再次提交则需要等待当天23:59:59后才可以重新提交
        //查询当日总数,并且返回最新的时间
        Map resMap = businessMapper.getRealNameLogCountOfDayAndLatestTime(userId);
        long count = (long) resMap.get("count");
        if (count > 5) {
            //隔天再试
            throw new BusinessException(RspEnmu.SUBMIT_TOO_FREQUENTLY_TOMORROW);
        }
        if (count > 3) {
            Date date = (Date) resMap.get("createTime");
            String dateBetween = DateUtils.getDateBetween(5, new Date(), date);
            Integer hour = Integer.valueOf(dateBetween);
            //实名提交4次之后等待60分钟后才能再次提交
            if (count == 4 && hour < 1) {
                throw new BusinessException(RspEnmu.SUBMIT_TOO_FREQUENTLY_60MINUTES);
            }
            //6小时后才能提交
            if (count == 5 && hour < 6) {
                throw new BusinessException(RspEnmu.SUBMIT_TOO_FREQUENTLY_6HOUR);
            }
        }
        return count;
    }

    //是否已实名
    private void realNameAuthIsEmpty(String accountNumber, String bvn, String userId) {
        if (extIdentityMapper.realNameAuthIsEmpty(userId, bvn, accountNumber) > 0) {
            throw new BusinessException(RspEnmu.REAL_NAME_AUTH_USED);
        }
    }

    public ExtIdentityResult getNubanVerifyInfo(BVNInfo bvnInfo, BankCode bankCode) {

        if (!kycApiSwitch(KycApiEnum.OKRA_IDENTITY_VERIFICATION)) {
            return null;
        }

        JSONObject apiRes;
        String userId = getUserId();

        ExtIdentityResult extIdentityResult = getLocalExtIdentity(bvnInfo.getAccountNumber(), null);
        if (extIdentityResult != null) {
            kycExqApiThreadLocal.set(extIdentityResult.getApiName());
            return extIdentityResult;
        }
        if (Func.isEmpty(bankCode.getOkraBankId())) {
            log.info("OkraBankCode为空！");
            return null;
        }

        JSONObject reqData = new JSONObject();
        reqData.put("nuban", bvnInfo.getAccountNumber());
        reqData.put("bank", bankCode.getOkraBankId());
        String apiName = "NUBAN-Identity-Verification";
        this.appendReqExtApi(apiName);

        try {
            apiRes = externalApiService.invokePayStackApi("NUBAN-Identity-Verification", okraConfig.getNubanVerify(), "POST", reqData, userId);
        } catch (Exception e) {
            log.error("Okra 接口异常");
            if (e instanceof BusinessException && RspEnmu.NETWORK_BUSY.getCode().equals(((BusinessException) e).getCode())) {
                //超时调用备用接口
                log.error("Okra 接口异常，尝试更换备用服务器API");
                apiName = "NUBAN-Identity-Verification-back";
                this.appendReqExtApi(apiName);
                try {
                    apiRes = externalApiService.invokePayStackApi("NUBAN-Identity-Verification-back", okraConfig.getBackNubanVerify(), "POST", reqData, userId);
                } catch (Exception exception) {
                    //接口网络异常
                    if (e instanceof BusinessException && RspEnmu.NETWORK_BUSY.getCode().equals(((BusinessException) e).getCode())) {
                        throw new BusinessException(RspEnmu.EXT_API_EXCEPTION_OKRA);
                    }
                    throw new BusinessException(RspEnmu.REAL_NAME_CHECK_FAILED_BY_OKRA_BACK);
                }
            } else {
                log.error("Okra服务器API异常", e);
                throw new BusinessException(RspEnmu.REAL_NAME_CHECK_FAILED_BY_OKRA);
            }
        }
        return dealIdentityVerifyResult(userId, apiRes, bvnInfo, apiName);
    }

    /**
     * 实名接口统一处理
     *
     * @return
     */
    private boolean apiExceptionHandle(KycApiEnum apiEnum, RspEnmu timeOutEnmu, Exception e) {
        //接口网络异常
        if (e instanceof BusinessException && RspEnmu.NETWORK_BUSY.getCode().equals(((BusinessException) e).getCode())) {
            BusinessException exception = (BusinessException) e;
            //没有后续扭转接口时抛出接口异常重试
            if (businessMapper.dictValueFollowUp(apiEnum.getValue()) == 0) {
                throw new BusinessException(timeOutEnmu);
            }
        }
        return true;
    }


    public ExtIdentityResult getOkraBvnVerifyInfo(BVNInfo bvnInfo, boolean bvnMatchMock) {

        if (!kycApiSwitch(KycApiEnum.OKRA_BVN_VERIFY)) {
            return null;
        }

        JSONObject reqBvn = new JSONObject();
        reqBvn.put("currency", "NGN");
        reqBvn.put("bvn", bvnInfo.getBvnAccount());
        try {
            String apiName = "Okra-bvn-verify";
            this.appendReqExtApi(apiName);
            JSONObject apiRes = externalApiService.invokePayStackApi(apiName, okraConfig.getBvnVerify(), "POST", reqBvn, getUserId());
            ExtIdentityResult extIdentityResult = dealIdentityVerifyResult(getUserId(), apiRes, bvnInfo, apiName);

            //如paystack - verify BVN match接口关闭，则需校验用户填写的姓名和CRS接口返回的姓名：
            if (!bvnMatchMock) {
                String basisName = bvnInfo.getFirstName().trim() + " " + bvnInfo.getMiddleName().trim() + " " + bvnInfo.getLastName().trim();
                RealNameServiceImpl.verifyName(basisName, extIdentityResult.getFullname());
            }
            return extIdentityResult;
        } catch (Exception e) {
            log.error("Okra-Bvn 接口异常", e);
            //verify BVN match接口关闭，姓名校验失败
            if (e instanceof BusinessException && RspEnmu.REAL_NAME_CHECK_FAILED_BY_NAME.getCode().equals(((BusinessException) e).getCode())) {
                throw e;
            }
            this.apiExceptionHandle(KycApiEnum.OKRA_BVN_VERIFY, RspEnmu.EXT_API_EXCEPTION_OKRA_BVN, e);
            return null;
        }
    }

    public ExtIdentityResult getCrsVerifyInfo(BVNInfo bvnInfo, boolean bvnMatchMock) {

        if (!kycApiSwitch(KycApiEnum.CRS_FIND_BVN2)) {
            return null;
        }

        String userId = getUserId();
        //查询crs 保存实名信息到extResult表 异步调用征信报告并保存
        ExtIdentityResult extIdentityResult = getLocalExtIdentity(null, bvnInfo.getBvnAccount());
        if (extIdentityResult != null) {
            kycExqApiThreadLocal.set(extIdentityResult.getApiName());
            return extIdentityResult;
        }
        try {
            this.appendReqExtApi("CRS_FindByBvn2");
            ExtIdentityResult extIdentifyResult = crsService.findByBvn2(userId, bvnInfo.getBvnAccount());
            extIdentifyResult.setUserId(userId);
            extIdentifyResult.setCustNo(getCustNo(extIdentifyResult.getUserId()));
            extIdentifyResult.setBvn(bvnInfo.getBvnAccount());
            extIdentifyResult.setAccountNumber(bvnInfo.getAccountNumber());
            extIdentifyResult.setBankCode(bvnInfo.getBankCode());
            //手机号码为空
            if (Func.isEmpty(extIdentifyResult.getPhones())) {
                extIdentifyResult.setState(BizConstants.REAL_NAME_LOG_FAIL);
            }

            extIdentityResultService.insertExtIdentityResult(extIdentifyResult);

            //异步保存报告
            crsService.createCrsReportAsync(extIdentifyResult.getCrsRegistryId(), userId, extIdentifyResult.getBvn());

            //如paystack - verify BVN match接口关闭，则需校验用户填写的姓名和CRS接口返回的姓名：
            if (!bvnMatchMock) {
                String basisName = bvnInfo.getFirstName().trim() + " " + bvnInfo.getMiddleName().trim() + " " + bvnInfo.getLastName().trim();
                RealNameServiceImpl.verifyName(basisName, extIdentifyResult.getFullname());
            }

            return extIdentifyResult;
        } catch (Exception e) {
            //verify BVN match接口关闭，姓名校验失败
            if (e instanceof BusinessException) {
                BusinessException be = (BusinessException) e;
                log.error("实名认证查询Crs信息异常, errMsg = {}, bvnInfo = {}", be.getDetails(), bvnInfo);
                if (RspEnmu.REAL_NAME_CHECK_FAILED_BY_NAME.getCode().equals(be.getCode())) {
                    throw e;
                }
            }

            log.error("实名认证查询Crs信息异常, bvnInfo:{}", bvnInfo, e);

            //接口异常
            this.apiExceptionHandle(KycApiEnum.CRS_FIND_BVN2, RspEnmu.EXT_API_EXCEPTION_CRS_BVN2, e);
            return null;
        }
    }

    private ExtIdentityResult getExtIdentityResult(String userId, JSONObject apiRes, String accountNumber, String bankCode) {
        ExtIdentityResult extIdentifyResult = new ExtIdentityResult();
        extIdentifyResult.setUserId(userId);
        extIdentifyResult.setCustNo(getCustNo(userId));
        extIdentifyResult.setAccountNumber(accountNumber);
        extIdentifyResult.setBankCode(bankCode);

        JSONObject identity = apiRes.getJSONObject("identity");
        if (identity == null) {
            identity = apiRes;
        }
        if (identity != null && identity.size() > 0) {

            //逗号转空格
            extIdentifyResult.setFullname(getValue(identity, "fullname"));
            extIdentifyResult.setFirstname(CheckAccountNameUtil.commaConvertSpace(getValue(identity, "firstname")));
            extIdentifyResult.setMiddlename(CheckAccountNameUtil.commaConvertSpace(getValue(identity, "middlename")));
            extIdentifyResult.setLastname(CheckAccountNameUtil.commaConvertSpace(getValue(identity, "lastname")));

            extIdentifyResult.setGender(getValue(identity, "gender"));
            extIdentifyResult.setDob(getValue(identity, "dob"));
            extIdentifyResult.setBvn(getValue(identity, "bvn"));
            extIdentifyResult.setVerifyStatus(getValue(identity, "status"));

            extIdentifyResult.setScore(getValue(identity, "score"));
            extIdentifyResult.setEnv(getValue(identity, "env"));
            extIdentifyResult.setLastUpdated(getValue(identity, "last_updated"));
            extIdentifyResult.setPhotoId(getValue(identity, "photo_id"));

            JSONArray phoneArray = identity.getJSONArray("phone");
            if (Func.isNotEmpty(phoneArray) && phoneArray.size() > 0) {
                //数组
                if (phoneArray.toString().startsWith("[")) {
                    extIdentifyResult.setPhone1(phoneArray.getString(0));
                    if (phoneArray.size() > 1) {
                        extIdentifyResult.setPhone2(phoneArray.getString(1));
                    }
                    List<String> phoneList = phoneArray.toJavaList(String.class);
                    String phones = phoneList.stream().collect(Collectors.joining(","));
                    extIdentifyResult.setPhones(phones);
                } else {
                    extIdentifyResult.setPhone1(phoneArray.toString());
                }
            }

            JSONArray emailArray = identity.getJSONArray("email");
            if (Func.isNotEmpty(emailArray) && emailArray.size() > 0) {
                //数组
                if (emailArray.toString().startsWith("[")) {
                    extIdentifyResult.setEmail1(emailArray.getString(0));
                    if (emailArray.size() > 1) {
                        extIdentifyResult.setEmail2(emailArray.getString(1));
                    }
                } else {
                    extIdentifyResult.setEmail1(emailArray.toString());
                }

            }

            extIdentifyResult.setVerified(getValue(identity, "verified"));
            extIdentifyResult.setCreatedAt(getValue(identity, "created_at"));
            extIdentifyResult.setRecord(getValue(apiRes, "record"));

//            JSONArray address = identity.getJSONArray("address");
//            if(address.size() > 0) {
//                 extIdentifyResult.setAddress1(address.getJSONObject(0).getString("address"));
//                if(address.size() > 1) {
//                    extIdentifyResult.setAddress2(address.getJSONObject(0).getString("address"));
//                }
//            }
        }

        JSONObject receipt = new JSONObject();
        if (checkReceipt(apiRes)) {
            receipt = apiRes.getJSONObject("receipt").getJSONObject("data").getJSONObject("receipt");
            extIdentifyResult.setOwner(getValue(receipt, "owner"));
            extIdentifyResult.setPlanType(getValue(receipt, "plan_type"));
            extIdentifyResult.setCharge(getValue(receipt, "charge"));
            extIdentifyResult.setMethod(getValue(receipt, "method"));
            extIdentifyResult.setWalletBalance(getValue(receipt, "wallet_balance"));
            extIdentifyResult.setType(getValue(receipt, "type"));
            extIdentifyResult.setBillingstatus(getValue(receipt, "billingStatus"));
            extIdentifyResult.setPlanTerm(getValue(receipt, "plan_term"));
            extIdentifyResult.setArchived(getValue(receipt, "archived"));
            extIdentifyResult.setPaid(getValue(receipt, "paid"));
            extIdentifyResult.setCurrency(getValue(receipt, "currency"));
        }

        extIdentifyResult.setAccount(getValue(apiRes, "account"));
        extIdentifyResult.setCustomer(getValue(apiRes, "customer"));
        return extIdentifyResult;
    }

    private void appendReqExtApi(String apiName) {
        String api = kycExqApiThreadLocal.get();
        if (Func.isNotEmpty(api)) {
            kycExqApiThreadLocal.set(api + "," + apiName);
            return;
        }
        kycExqApiThreadLocal.set(apiName);
    }


    @Override
    public void processCreditApply(CreditApplyReq creditApplyReq) {
        Integer step = creditApplyReq.getStep();
        String userId = getUserId();
        CustomerInfo custInfo = getCustInfoByUserId(userId);
        String applySerialNo = creditApplyReq.getApplySerialNo();

        checkRealName(custInfo.getCustNo());

        //授信失败30天内不能再次授信
        checkRiskCredit(userId);

        //当前版本是否>=2.2.0
        boolean newVersion = VersionUtils.compareVersion(getAppVersion(), "2.2.0") >= 0;
        List<CreditApply> creditApplyList = getCreditApplyList(applySerialNo);
        if (step == 1) {
            CreditApplyReqA creditApplyReqA = creditApplyReq.getCreditApplyReqA();

            creditApplyReqA.setRelateName(Func.filterEmoji(creditApplyReqA.getRelateName()));
            creditApplyReqA.setRelateName2(Func.filterEmoji(creditApplyReqA.getRelateName2()));

            if (creditApplyList.size() > 0) {
                updateCreditApplyBySerialNo(creditApplyReq.getCreditApplyReqA(), applySerialNo, BizConstants.APPLY_STATUS_11_INFO_ENTRY_SUPPLEMENT);
            } else {
                saveCreditApply(creditApplyReq, userId, custInfo.getCustNo(), newVersion);
                saveDeviceInfo(creditApplyReqA.getDeviceInfo(), applySerialNo);
                RulePremiseVo rulePremiseVo = RulePremiseVo.builder().custNo(getCustNo()).type(RuleStrategy.CREDIT).serialNo(applySerialNo).build();
                premiseService.rulePremise(rulePremiseVo, "deviceFlag");
            }
            //当前版本小于2.2.0(旧版本) 且 如果职业不为学生、无业 则直接返回
            if (!newVersion && !specialOccupationsList.contains(creditApplyReqA.getOccupation())) {
                return;
            }
        }

        if (step == 2) {
            if (creditApplyList.size() == 0) {
                throw new BusinessException(RspEnmu.NO_RECORD);
            }
            updateCreditApplyBySerialNo(creditApplyReq.getCreditApplyReqB(), applySerialNo, BizConstants.APPLY_STATUS_9_APPROVALING);
        }

        RulePremiseVo rulePremiseVo = RulePremiseVo.builder().custNo(getCustNo()).type(RuleStrategy.CREDIT).serialNo(applySerialNo).build();
        premiseService.rulePremise(rulePremiseVo, "orderFlag");
    }

    @Override
    public void processCreditApplyV228(CreditApplyReq creditApplyReq) {
        String userId = getUserId();
        CustomerInfo custInfo = getCustInfoByUserId(userId);
        String applySerialNo = creditApplyReq.getApplySerialNo();

        checkRealName(custInfo.getCustNo());

        //授信失败30天内不能再次授信
        checkRiskCredit(userId);

        List<CreditApply> creditApplyList = getCreditApplyList(applySerialNo);
        CreditApplyReqA creditApplyReqA = creditApplyReq.getCreditApplyReqA();

        creditApplyReqA.setRelateName(Func.filterEmoji(creditApplyReqA.getRelateName()));
        creditApplyReqA.setRelateName2(Func.filterEmoji(creditApplyReqA.getRelateName2()));

        if (creditApplyList.size() > 0) {
            updateCreditApplyBySerialNo(creditApplyReq.getCreditApplyReqA(), applySerialNo, BizConstants.APPLY_STATUS_11_INFO_ENTRY_SUPPLEMENT);
        } else {
            saveCreditApply(creditApplyReq, userId, custInfo.getCustNo(), true);
            //保存授信申请后发起触发分销活动奖励
            awardDistributionService.distributionAwardTrigger(AwardEventEnum.SUBMIT_CREDIT_APPLY, userId, null);
            saveDeviceInfo(creditApplyReqA.getDeviceInfo(), applySerialNo);

            RulePremiseVo rulePremiseVo = RulePremiseVo.builder().custNo(getCustNo()).type(RuleStrategy.CREDIT).userId(getUserId()).serialNo(applySerialNo).fingerprint(creditApplyReqA.getDeviceInfo().getString("newFingerprint")).build();
            premiseService.rulePremiseV228(rulePremiseVo);
        }
    }

    @Override
    public void contactCheck(CreditApplyReq creditApplyReq) {
        if (Func.isEmpty(creditApplyReq)) {
            return;
        }
        CreditApplyReqA creditApplyReqA = creditApplyReq.getCreditApplyReqA();
        String relatePhone = Func.isEmpty(creditApplyReqA.getRelatePhone()) ? creditApplyReqA.getRelatePhone() : StringUtils.substringAfter(creditApplyReqA.getRelatePhone(), 10);
        String relatePhone2 = Func.isEmpty(creditApplyReqA.getRelatePhone2()) ? creditApplyReqA.getRelatePhone2() : StringUtils.substringAfter(creditApplyReqA.getRelatePhone2(), 10);

        //注册手机号
        String registerPhone = StringUtils.substringAfter(getAppUser().getPhone(), 10);
        if (registerPhone.equals(relatePhone) || registerPhone.equals(relatePhone2)) {
            throw new BusinessException(RspEnmu.RAISE_LIMIT_PHONE_REPEAT1);
        }
        //bvn号码
        RealNameAuth realNameAuth = getRealNameAuth();
        String bvnPhone = StringUtils.substringAfter(realNameAuth.getPhoneNumber(), 10);
        if (bvnPhone.equals(relatePhone) || bvnPhone.equals(relatePhone2)) {
            throw new BusinessException(RspEnmu.RAISE_LIMIT_PHONE_REPEAT1);
        }
    }

    /**
     * 调用规则引擎 前置校验通过
     *
     * @param applySerialNo
     */
    @Override
    public void rulePass(String applySerialNo) {

        log.info("调用规则引擎,流水号：{}", applySerialNo);
        List<CreditApply> creditApplyList = getCreditApplyList(applySerialNo);
        if (creditApplyList.size() == 0) {
            throw new BusinessException(RspEnmu.BANK_LIST_RSP_NULL);
        }
        CreditApply apply = creditApplyList.get(0);

        AppUser appUser = new AppUser();
        appUser.setUserUuid(apply.getUserId());
        List<AppUser> appUsers = appUserService.selectAppUserList(appUser);
        if (appUsers.size() == 0) {
            throw new BusinessException(RspEnmu.USERID_IS_EMPTY);
        }
        appUser = appUsers.get(0);

        CreditApply creditApply = new CreditApply();
        creditApply.setId(apply.getId());
        try {
            // 调用规则引擎
//            JSONObject ruleRst = ruleEngineService.riskRuleEngine(RiskConstants.RULE_TYPE_APPLY, applySerialNo);
            JSONObject ruleRst = restTemplateService.riskRuleEngine(RiskConstants.RULE_TYPE_APPLY, applySerialNo);
            //保存风控需要的字段到客户信息表
            creditAccountInfoService.updateCustomerByRuleResult(ruleRst, apply.getCustNo(), RiskConstants.RULE_TYPE_APPLY);

            String ruleResult = ruleRst.getString(RiskConstants.RULE_RESULT_DEAL_TYPE_KEY);
            //风控拒绝
            if (RiskConstants.RULE_RESULT_REJECT.equals(ruleResult)) {
                creditApply.setApplyStatus(BizConstants.APPLY_STATUS_0_REFUSED);
                creditApply.setRemark("Rule reject");
                creditApply.setFreezeDate(ruleRst.getString(RiskConstants.RULE_RESULT_APPLY_FREEZE));
                creditApplyService.updateCreditApply(creditApply);

                asyncService.apdUserTag(AppUserMark.builder().userId(apply.getUserId()).build(), UserTagEnum.CREDIT_APPLICATION_FAILED_RISK_REJECT);
                return;
            }

            //中断
            if (RiskConstants.RULE_RESULT_REVOKE.equals(ruleResult)) {
                throw new BusinessException(RspEnmu.OTP_REQUEST);
            }

            //未知结果
            if (!RiskConstants.RULE_RESULT_PASS.equals(ruleResult)) {
                throw new BusinessException(RspEnmu.OTP_REQUEST);
            }

            //新增奖励用户分组
            if ("1".equals(ruleRst.getString(RiskConstants.RULE_AWARD_QUALIFIED))) { //等于1为有资格，则插入用户标签组
                userActivityGroupService.insertUserActivityGroup(appUser.getUserUuid(), "USER_TAG");
            }

            creditApply.setApplyStatus(BizConstants.APPLY_STATUS_1_PASSED);
            creditApplyService.updateCreditApply(creditApply);

            // 更新客户信息
            updateCustomer(apply.getId(), getCustInfoByUserId(apply.getUserId()));

            // 创建授信账户及额度
            BigDecimal creditLine = ruleRst.getBigDecimal(RiskConstants.RULE_RESULT_FINAL_LOAN_KEY);
            BigDecimal channelLine = ruleRst.getBigDecimal(RiskConstants.RULE_RAISE_PASS_CHANNEL_LIMIT);

            //是否增加渠道额度
            AppUser validInviter = appService.getValidInviter(appUser.getRecommendCode());
            if (validInviter != null) {
                creditLine = creditLine.add(channelLine);
            } else {
                channelLine = BigDecimal.ZERO;
            }
            createCreditAccount(apply.getCustNo(), creditLine, channelLine);
            //初始化额度变更日志表
            increasedLimitLogService.insertIncreasedLimitLog(new IncreasedLimitLogVo(apply.getCustNo(), creditLine, new Date()));

            //用户标签-授信成功
            //creditUserTag(appUser.getUserUuid(), creditLine);
            asyncService.apdUserTag(AppUserMark.builder().userId(appUser.getUserUuid()).creditLine(creditLine).build(),
                    bankAccountService.countByCustNo(appUser.getUserUuid()) > 0 ?
                            UserTagEnum.CREDIT_APPLICATION_SUCCESS_ACCOUNT_EXISTED : UserTagEnum.CREDIT_APPLICATION_SUCCESS_ACCOUNT_NOT_EXIST);

            //发送授信成功及时短信
            asyncService.sendCreditPassSms(apply, appUser, creditLine, channelLine, validInviter);

            //奖励分销活动
            awardDistributionService.distributionAwardTrigger(AwardEventEnum.CREDIT_APPLY_SUCCESS, apply.getUserId(), null);

        } catch (Exception e) {
            log.info("授信异常抛出：{}", e);
            asyncService.apdUserTag(AppUserMark.builder().userId(appUser.getUserUuid()).build(), UserTagEnum.CREDIT_APPLICATION_FAILED);
            throw e;
        }
    }

//    private void sendCreditPassSms(CreditApply apply, AppUser appUser, BigDecimal creditLine, BigDecimal channelLine, AppUser validInviter) {
//        JSONObject smsParam = new JSONObject();
//        //判断当前用户是否满足 0利息/折扣利息 产品
//        String smsType = null;
//        List<ProductInfo> productInfo = creditAccountService.selectCreditAccountProduct(apply.getCustNo());
//        productInfo.stream().sorted(Comparator.comparing(obj-> obj.getTags().contains("-1")? 1:0)).collect(Collectors.toList());
//        if (productInfo.size() > 0) {
//            for (int i = 0; i < productInfo.size(); i++) {
//                String params = productInfo.get(i).getTags();
//                smsParam.put("last_name", getRealNameAuth(appUser.getUserUuid()).getLastName());
//                smsParam.put("credit_line", String.valueOf(creditLine.setScale(2, BigDecimal.ROUND_HALF_UP)));
//                smsParam.put("credit_rating", sysConfigService.selectConfigKey(productInfo.get(i).getCreditRating()));
//                if (params.contains("-1")) {
//                    smsParam.put("productperiod", productInfo.get(i).getPeriodNum());
//                    smsParam.put("tags", "0 interest");
//                } else if(params.contains("%")){
//                    smsParam.put("productperiod", productInfo.get(i).getPeriodNum());
//                    BigDecimal multiply = ((productInfo.get(i).getShowInterestRate().subtract(productInfo.get(i).getInterestRate())).divide(productInfo.get(i).getShowInterestRate(),BigDecimal.ROUND_HALF_UP)).multiply(BigDecimal.valueOf(100)).setScale(0,BigDecimal.ROUND_HALF_UP);
//                    smsParam.put("tags", multiply + "% interest discount");
//                }
//                smsType = "main_cr_succ_0ordiscint";
//                break;
//            }
//        } else {
//            //渠道额度为0时发送无邀请人短信
//            //JSONObject smsParam = new JSONObject();
//            smsParam.put("lastName", getRealNameAuth(appUser.getUserUuid()).getLastName());
//            smsParam.put("creditLine", String.valueOf(creditLine.setScale(2, BigDecimal.ROUND_HALF_UP)));
//            smsType = BizConstants.SMS_TYPE_CREDIT_PASS;
//            //渠道额度不为0时发送邀请人短信
//            if (!channelLine.equals(BigDecimal.ZERO)) {
//                //最终额度 - 渠道额度
//                //基础额度
//                smsParam.put("creditLine", String.valueOf(creditLine.subtract(channelLine).setScale(2, BigDecimal.ROUND_HALF_UP)));
//                smsParam.put("inviterNickname", validInviter.getNickName());
//                smsParam.put("channelQuota", String.valueOf(channelLine.setScale(2, BigDecimal.ROUND_HALF_UP)));
//                smsParam.put("finalQuota", String.valueOf(creditLine.setScale(2, BigDecimal.ROUND_HALF_UP)));
//                smsType = BizConstants.SMS_TYPE_INVITER_CREDIT;
//            }
//        }
//        SmsSendData smsData = SmsSendData.builder().smsType(smsType).noticeType(BizConstants.SMS_NOTICE_TYPE_NOTICE).countryCode(appUser.getCountryCode()).phoneNumber(appUser.getPhone()).smsParam(smsParam).build();
//        asyncService.sendSms(smsData);
//    }

    private void creditUserTag(String userId, BigDecimal creditLine) {
//        UserTagEnum userTagEnum = UserTagEnum.CREDIT_APPLICATION_SUCCESS_ACCOUNT_NOT_EXIST;
//        if (bankAccountService.countByCustNo(userId) > 0) {
//            userTagEnum = UserTagEnum.CREDIT_APPLICATION_SUCCESS_ACCOUNT_EXISTED;
//        }
//        asyncService.apdUserTag(AppUserMark.builder().userId(userId).creditLine(creditLine).build(), userTagEnum);
    }


    @Override
    public boolean creditLineHistoryInit() {
        //删除初始化提额
        IncreasedLimitLog increasedLimitLog = new IncreasedLimitLog();
        increasedLimitLog.setAdjustType("0");
        List<Long> ids = increasedLimitLogService.selectIncreasedLimitLogList(increasedLimitLog).stream().map(IncreasedLimitLog::getId).collect(Collectors.toList());

        if (ids.size() > 0) {
            increasedLimitLogService.deleteIncreasedLimitLogByIds(ids.toArray(new Long[ids.size()]));
        }

        List<CreditAccount> creditAccounts = creditAccountService.selectCreditAccountList(new CreditAccount());
        List<IncreasedLimitLog> increasedLimitLogs = new ArrayList<>();
        creditAccounts.forEach(res -> {
            increasedLimitLogs.add(new IncreasedLimitLogVo(res.getCustNo(), res.getCreditLine(), res.getCreateTime()));
        });
        return bCreditAccountMapper.insertIncreasedLimitLogBatch(increasedLimitLogs) > 0;
    }

    private void checkRealName(String custNo) {
        RealNameAuth realNameAuth = iRealNameAuthService.selectRealNameAuthByCustNo(custNo);
        if (realNameAuth == null || realNameAuth.getId() == null) {
            throw new BusinessException(RspEnmu.REAL_NAME_NOTFOUND);
        }
    }

    private void checkRiskCredit(String userId) {

        QueryWrapper<CreditApply> queryWrapper = new QueryWrapper<CreditApply>().eq("user_id", userId).last("order by id desc limit 1");
        CreditApply creditApply = creditApplyService.getOne(queryWrapper);
        if (Func.isEmpty(creditApply)) {
            return;
        }

        if (BizConstants.APPLY_STATUS_9_APPROVALING == creditApply.getApplyStatus()) {
            throw new BusinessException(RspEnmu.SUBMITTED_APPLICATION);
        }
        if (BizConstants.APPLY_STATUS_1_PASSED == creditApply.getApplyStatus()) {
            throw new BusinessException(RspEnmu.SUCCESS_APPLICATION);
        }

        //授信失败
        if (BizConstants.APPLY_STATUS_0_REFUSED == creditApply.getApplyStatus()) {
            //授信失败30天内不能再次授信
            int freezeDay = 30;
            String freezeDate = creditApply.getFreezeDate();
            if (freezeDate != null) {
                freezeDay = Integer.valueOf(freezeDate);
            }
            double day = Func.dayDifference(creditApply.getCreateTime());
            if (day < freezeDay) {
                throw new BusinessException(RspEnmu.NOT_QUALIFIED_APPLICATION);
            }
        }

    }

    private boolean checkReceipt(JSONObject apiRes) {
        if (apiRes.get("receipt") == null || apiRes.getJSONObject("receipt").size() <= 0) {
            return false;
        }

        if (apiRes.getJSONObject("receipt").get("data") == null || apiRes.getJSONObject("receipt").getJSONObject("data").size() <= 0) {
            return false;
        }

        if (apiRes.getJSONObject("receipt").getJSONObject("data").get("receipt") == null || apiRes.getJSONObject("receipt").getJSONObject("data").getJSONObject("receipt").size() <= 0) {
            return false;
        }

        return true;
    }


    private String getGender(String desGender, String gender) {
        if (StringUtils.isNotBlank(desGender) && !StringUtils.equals(desGender, "-999-")) {
            return desGender;
        }
        return gender;
    }


    private String getValue(JSONObject json, String fieldName) {
        return json.get(fieldName) == null ? "" : json.getString(fieldName);
    }

    private void updateCreditApply(Long applyId, CreditApplyReq creditApplyReq) {
        CreditApplyReqA creditApplyReqA = creditApplyReq.getCreditApplyReqA();
        CreditApply creditApply = new CreditApply();
        BeanUtils.copyBeanProp(creditApply, creditApplyReqA);
        creditApply.setId(applyId);
        creditApply.setApplyStatus(BizConstants.APPLY_STATUS_11_INFO_ENTRY_SUPPLEMENT);
        creditApplyService.updateCreditApply(creditApply);
    }


    private void updateCreditApplyBySerialNo(CreditApplyBase<? extends CreditApplyBase> creditApplyBase, String applySerialNo, int applyStatus) {
        //根据流水号修改
        CreditApply creditApply = new CreditApply();
        BeanUtils.copyBeanProp(creditApply, creditApplyBase);
        creditApply.setApplyStatus(applyStatus);
        creditApplyService.update(creditApply, new QueryWrapper<CreditApply>().eq("apply_serial_no", applySerialNo));
    }


    private CreditApply getCreditApply(String applySerialNo) {
        List<CreditApply> creditApplyList = getCreditApplyList(applySerialNo);
        if (CollectionUtils.isEmpty(creditApplyList)) {
            throw new BusinessException(RspEnmu.NO_RECORD);
        }
        return creditApplyList.get(0);
    }

    private List<CreditApply> getCreditApplyList(String applySerialNo) {
        CreditApply apply = new CreditApply();
        apply.setApplySerialNo(applySerialNo);
        apply.setState(BizConstants.BIZ_STATUS_1);
        List<CreditApply> creditApplyList = creditApplyService.selectCreditApplyList(apply);
        return creditApplyList;
    }

    private void saveDeviceInfo(JSONObject deviceInfo, String actionKey) {
        CustomerInfo customerInfo = getCustInfoByUserId(getUserId());
        appService.saveAppDeviceInfo(getUserId(), customerInfo.getCustNo(), BizConstants.ACTION_TYPE_APPLY, actionKey, deviceInfo);
    }

    /**
     * @param creditApplyReq
     * @param userId
     * @param custNo
     * @param newVersion     版本是否大于2.2.0
     */
    private void saveCreditApply(CreditApplyReq creditApplyReq, String userId, String custNo, boolean newVersion) {
        CreditApplyReqA creditApplyReqA = creditApplyReq.getCreditApplyReqA();
        CreditApply creditApply = new CreditApply();
        BeanUtils.copyBeanProp(creditApply, creditApplyReqA);
        creditApply.setUserId(userId);
        creditApply.setCustNo(custNo);
        creditApply.setApplySerialNo(creditApplyReq.getApplySerialNo());
        creditApply.setApplyStatus(BizConstants.APPLY_STATUS_11_INFO_ENTRY_SUPPLEMENT);
        if (newVersion || specialOccupationsList.contains(creditApplyReqA.getOccupation())) {
            creditApply.setApplyStatus(BizConstants.APPLY_STATUS_9_APPROVALING);
        }
        creditApplyService.insertCreditApply(creditApply);
    }

    private int updateCustomer(Long applyId, CustomerInfo custInfo) {
        CreditApply creditApply = creditApplyService.selectCreditApplyById(applyId);

        CustomerInfo customerInfo = new CustomerInfo();
        BeanUtils.copyProperties(creditApply, customerInfo);

        customerInfo.setDegreeUpdated(new Date());
        customerInfo.setOccupationUpdated(new Date());
        customerInfo.setRelateUpdated(new Date());
        customerInfo.setId(custInfo.getId());
        return customerInfoService.updateCustomerInfo(customerInfo);
    }

    private int createCreditAccount(String custNo, BigDecimal creditLine, BigDecimal channelLine) {
        CreditAccount creditAccount = new CreditAccount();
        creditAccount.setCustNo(custNo);
        creditAccount.setCreditLine(creditLine);
        creditAccount.setCreditDate(new Date());
        creditAccount.setChannelQuota(channelLine);
        creditAccount.setOnLoan("0"); // 在贷
        return creditAccountService.insertCreditAccount(creditAccount);
    }

    public ExtIdentityResult identityVerification(BVNInfo bvnInfo, BankCode bankCode, boolean isCheckBvn) {

        //校验bvn account匹配
        //临时mock mock打开时关闭bvnMatch
        boolean bvnMatchMock = kycApiSwitch(KycApiEnum.PAY_STACK_BVN_MATCH);
        if (bvnMatchMock && isCheckBvn) {
            CustBvnMatch custBvnMatch;
            try {
                custBvnMatch = payTransService.verifyBVNMatch(bvnInfo.getAccountNumber(), bvnInfo.getBvnAccount(), bvnInfo.getBankCode(), getUserId());
            } catch (Exception e) {
                //OkraAccount是否打开，没打开则直接抛出异常
                log.error("BvnMatch请求异常", e);
                if (kycApiSwitch(KycApiEnum.OKRA_IDENTITY_VERIFICATION)) {
                    return getNubanVerifyInfo(bvnInfo, bankCode);
                }
                throw new BusinessException(RspEnmu.EXT_API_EXCEPTION_PAY_STACK_BVN_MATCH);
            }
            verifyBvn(custBvnMatch);
        }
        //CrsBVn
        ExtIdentityResult extIdentifyResult = getCrsVerifyInfo(bvnInfo, bvnMatchMock);
        if (extIdentifyResult == null) {
            //OkraBvn
            extIdentifyResult = getOkraBvnVerifyInfo(bvnInfo, bvnMatchMock);
            if (extIdentifyResult == null) {
                //okraAccount
                extIdentifyResult = getNubanVerifyInfo(bvnInfo, bankCode);
            }
        }
        return extIdentifyResult;
    }

    private void verifyField(BVNInfo bvnInfo, ExtIdentityResult extIdentifyResult) {
        if (extIdentifyResult == null) {
            log.info("实名数据为空，认证失败！");
            asyncService.apdUserTag(AppUserMark.builder().userId(getUserId()).build(), UserTagEnum.IDENTITY_VERIFIED_FAILED_MISS_INFO);
            throw new BusinessException(RspEnmu.CYC_INFORMATION_NOT_FOUND);
        }
        //校验bvn
        if (!extIdentifyResult.getBvn().equals(bvnInfo.getBvnAccount())) {
            throw new BusinessException(RspEnmu.REAL_NAME_CHECK_FAILED_BY_BVN_2);
        }
        //生日或手机号码为空 实名失败！
        //Func.hasEmpty(extIdentifyResult.getPhones(), extIdentifyResult.getDob()) 更新 -> 只校验手机号是否为空
        if (Func.isEmpty(extIdentifyResult.getPhones())) {
            throw new BusinessException(RspEnmu.REAL_NAME_CHECK_FAILED_BIRTH_PHONE);
        }
        //校验 ext_Dob和用户输入的Dob || 注册手机号与BVN手机号不一致
        //return verifyIdentity(bvnInfo, extIdentifyResult);
    }

    private String addZero(String phone) {
        if (phone.length() == 11) {
            return phone;
        }
        return "0" + phone;
    }

    // 匹配不一致返回空
    private String matchPhone(String curPhone, String... phones) {
        //校验后10位号码
        curPhone = StringUtils.substringAfter(curPhone, 10);
        String res = null;
        for (int i = 0; i < phones.length; i++) {
            String phone = StringUtils.substringAfter(phones[i], 10);
            if (curPhone.equals(phone)) {
                res = curPhone;
                break;
            }
        }
        return res;
    }


    //校验生日 | 注册号码与BVN号码是否一致
    private JSONObject verifyIdentity(BVNInfo bvnInfo, ExtIdentityResult identityResult) {

        String customerNo = getCustNo();
        //注册手机号与BVN手机号不一致,返回状态码，进入重填BVN手机号页面
        StringBuilder str = new StringBuilder();
        String curPhone = getAppUser().getPhone();
        String matchPhone = matchPhone(curPhone, Func.toStrArray(identityResult.getPhones()));
        if (Func.isEmpty(matchPhone)) {
            str.append("30002");
        }
        JSONObject obj = new JSONObject();
        obj.put("unmatch", str);
        redisCache.setCacheObject(BizConstants.REAL_NAME_PHONE + customerNo, addZero(curPhone), 60 * 30, TimeUnit.SECONDS);
        log.info("设置PhoneKey,{}", BizConstants.REAL_NAME_PHONE + customerNo);
        return obj;

    }

    //bvn校验
    private void verifyBvn(CustBvnMatch custBvnMatch) {
        //进入黑名单 实名失败
        if ("true".equals(custBvnMatch.getIsBlacklisted())) {
            throw new BusinessException(RspEnmu.REAL_NAME_CHECK_FAILED);
        }
        //accountNumber不匹配
        if (Func.isEmpty(custBvnMatch.getAccountNumber()) || "false".equals(custBvnMatch.getAccountNumber())) {
            throw new BusinessException(RspEnmu.BVN_NOT_EQUALS_ACCOUNT_NUMBER);
        }
    }


    /**
     * @param apiEnum
     * @return
     */
    protected boolean kycApiSwitch(KycApiEnum apiEnum) {
        return "1".equals(sysDictDataService.selectDictLabel(apiEnum.getType(), apiEnum.getValue()));
    }


}
